/*
 * Vector.h
 *
 *  Created on: Oct 31, 2012
 *      Author: Carlos Alegría
 */

#ifndef VECTOR_H_
#define VECTOR_H_

#include <ostream>

using namespace std;

namespace poo
{

  /**
   * Un vector en un espacio euclidiano tridimensional.
   *
   * Esta clase implementa un vector en un espacio euclidiano tridimensional con
   * compoentes enteros. El objetivo de esta clase es mostrar las
   * características básicas que pueden utilizarse en clases en C++.
   */
  class Vector3D
  {
    // Funciones amigas
    //
    // Las funciones amigas son funciones "libres" (que no pertenecen a ninguna
    // clase) que tienen acceso a todos los miembros de una clase.
    //
    // Una función es declarada como amiga de una clase si se define dentro del
    // alcance de la clase, y se escribe la palabra reservada "friend" antes del
    // tipo de regreso de la función.
    //
    // Por convención, las declaraciones de funciones amigas deben escribirse
    // al principio de la definición de la clase, antes de los especificadores
    // de acceso.

    // Sobrecarga de operadores
    //
    // En C++ es posible definir el comportamiento de operadores binarios como
    // +, -, *, >>, <<, =, etc. y de operadores unarios como !, ++, --, etc.
    //
    // La sobrecarga de operadores se realiza definiendo funciones con el nombre
    // "operator<nombre de operador>". Si la implementación del operador
    // requiere acceder a los miembros protegidos o privados de alguno de sus
    // argumentos, la función debe ser miembro o una función amiga de las clases
    // involucradas en la operación. En caso contrario, la implementación del
    // operador puede ser una función libre.
    //
    // En la sobreescritura de operadores se deben seguir las siguientes reglas:
    //
    // - No está permitido redefinir el comportamiento de operadores sobre tipos
    //   de dato primitivos (int, float, double, etc..)
    //
    // - No está permitido cambiar la aridad natural de un operador (no es
    //   posible especificar que "+" recibe tres argumentos, o que "++" recibe
    //   dos argumentos, por ejemplo).
    //
    // - No es posible sobreescribir los operadores ".", ".*", "::", y "?:".

    /**
     * Resta entre dos vectores
     *
     * @param [in] v    El minuendo de la resta
     * @param [in] w    El substraendo de la resta
     * @returns         Un vector cuyos componentes se obtienen de restar los
     *                  componentes de <code>w</code> de los componentes de
     *                  <code>v</code>
     */
    friend Vector3D &
    operator-(const Vector3D &v, const Vector3D &w);

    /**
     * Inserción de un vector a un stream de salida.
     *
     * Escribe el contenido de <code>v</code> en el stream de salida
     * <code>w</code> con el formato "(comp x, comp y, comp z)".
     *
     * @param [in] out  El stream de salida en el que se escribirá el contenido
     *                  del vector
     * @param [in] v    El vector cuyo contenido será escrito en el stream de
     *                  salida
     * @returns         Una referencia al stream especificado como argumento
     */
    friend ostream &
    operator<<(ostream &out, const Vector3D &v);

  public:

    /**
     * Crea un Vector3D con los valores de los componentes especificados
     */
    //
    // los valores especificados en los argumentos son valores por default, que
    // se utilizan en caso de llamar al constructor sin argumentos.
    //
    Vector3D(int x = 0, int y = 0, int z = 0);

    /**
     * Destructor de la clase
     */
    ~Vector3D();

    // Funciones inline
    //
    // Cuando la implementación de una función es precedidas por la palabra
    // reservada "inline" su contenido substituido en el programa cuando se
    // realiza una llamada a la función, de forma similar a como se comportan
    // las macros definidas con "#define".
    //
    // La diferencia fundamental es que para las funciones inline, el compilador
    // si realiza verificación de tipos.
    //
    // Al definir métodos inline en una clase, las implementaciones deben estar
    // en el encabezado, junto con la definición de la clase, para que el compi
    // lador pueda realizar la substicución de texto donde se encuentran las
    // llamadas a las funciones inline.

    // Miembros constantes
    //
    // La palabra reservada "const" permite definir una variable que no puede
    // cambiar su valor. Cuando es utilizada con una variable que contiene un
    // objeto, indica al compilador que a través de esa referencia sólo es
    // posible utilizar métodos que no modifiquen el estado del objeto al que
    // apunta.
    //
    // Para indicar que un método no modifica el estado de un objeto, se utiliza
    // "const" en su declaración, como en los getters siguientes.

    /**
     * Obtiene el valor del componente sobre el eje X de este vector
     */
    inline int
    getX() const
    {
      return x;
    }

    /**
     * Obtiene el valor del componente sobre el eje Y de este vector
     */
    inline int
    getY() const
    {
      return y;
    }

    /**
     * Obtiene el valor del componente sobre el eje Z de este vector
     */
    inline int
    getZ() const;

    /**
     * Suma entre dos vectores.
     *
     * @param [in] p    El segundo sumando de la operación
     * @returns         Un vector cuyos componentes fueron obtenidos de sumar
     *                  los componentes del objeto actual con los componentes de
     *                  <code>p</code>
     */
    //
    // El operador de suma es definido como miembro de la clase Vector3D. En
    // este caso, sólo es necesario especificar un argumento. Si consideramos
    // que la función tiene acceso a los miembros del objeto actual, la
    // operación sigue siendo definida con dos argumentos, manteniendo su
    // aridad.
    //
    Vector3D &
    operator+(const Vector3D &p);

  private:

    int x; /**< La componente sobre el eje X del vector */
    int y; /**< La componente sobre el eje Y del vector */
    int z; /**< La componente sobre el eje Z del vector */
  };

  //
  // Las siguientes funciones se encuentran fuera de la declaración de la clase
  //

  // Este método es definido fuera de la declaración de la clase, por lo que
  // debemos utilizar el operador de resolución de alcance "::". Ya que el
  // método es inline, escribimos su definición en el mismo archivo del
  // encabezado de la clase (*.h)
  //
  int
  Vector3D::getZ() const
  {
    return z;
  }

  /**
   * Multiplicación entre un vector y un escalar
   *
   * @param [in] c      El escalar que será utilizado como multiplicando en la
   *                    operación
   * @param [in] v      El vector que será utilizado como multiplicando en la
   *                    operación
   * @returns           Un vector cuyos componentes fueron obtenidos de
   *                    multiplicar <code>c</code> por las componentes de
   *                    <code>v</code>
   */
  Vector3D &
  operator*(int c, const Vector3D &v);

} /* namespace poo */

#endif /* VECTOR_H_ */
